﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using IChessEngine;
using ChessEngine.Basic;

namespace ChessEngine.Gaming
{
    public class ChsPosition : IChsPosition
    {
        private static IChsBoard board = new ChsBoard();
        private static IChsValidator validator = new ChsValidator();
        private IChsPiece[,] pieces = null;
        private bool? allyKingCheck = null;
        private bool? enemyKingCheck = null;
        private bool whiteCastleQueenSide = true;
        private bool whiteCastleKingSide = true;
        private bool blackCastleQueenSide = true;
        private bool blackCastleKingSide = true;
        private bool? tempWhiteCastleQueenSide = null;
        private bool? tempWhiteCastleKingSide = null;
        private bool? tempBlackCastleQueenSide = null;
        private bool? tempBlackCastleKingSide = null;
        private EChsPieceColor colorToMove = EChsPieceColor.WHITE;
        public ChsPosition()
        {
            pieces = new IChsPiece[board.Width,board.Height];
            for (int i = 0; i < 8; i++)
            {
                pieces[i, 1] = new ChsPawn(EChsPieceColor.WHITE);
                pieces[i,6] = new ChsPawn(EChsPieceColor.BLACK);
            }
            pieces[ 0,0] = new ChsPiece(EChsPieceType.ROCK, EChsPieceColor.WHITE);
            pieces[ 1,0] = new ChsPiece(EChsPieceType.KNIGHT, EChsPieceColor.WHITE);
            pieces[ 2,0] = new ChsPiece(EChsPieceType.BISHOP, EChsPieceColor.WHITE);
            pieces[ 3,0] = new ChsPiece(EChsPieceType.QUEEN, EChsPieceColor.WHITE);
            pieces[ 4,0] = new ChsPiece(EChsPieceType.KING, EChsPieceColor.WHITE);
            pieces[ 5,0] = new ChsPiece(EChsPieceType.BISHOP, EChsPieceColor.WHITE);
            pieces[ 6,0] = new ChsPiece(EChsPieceType.KNIGHT, EChsPieceColor.WHITE);
            pieces[ 7,0] = new ChsPiece(EChsPieceType.ROCK, EChsPieceColor.WHITE);
            pieces[ 0,7] = new ChsPiece(EChsPieceType.ROCK, EChsPieceColor.BLACK);
            pieces[ 1,7] = new ChsPiece(EChsPieceType.KNIGHT, EChsPieceColor.BLACK);
            pieces[ 2,7] = new ChsPiece(EChsPieceType.BISHOP, EChsPieceColor.BLACK);
            pieces[ 3,7] = new ChsPiece(EChsPieceType.QUEEN, EChsPieceColor.BLACK);
            pieces[ 4,7] = new ChsPiece(EChsPieceType.KING, EChsPieceColor.BLACK);
            pieces[ 5,7] = new ChsPiece(EChsPieceType.BISHOP, EChsPieceColor.BLACK);
            pieces[ 6,7] = new ChsPiece(EChsPieceType.KNIGHT, EChsPieceColor.BLACK);
            pieces[ 7,7] = new ChsPiece(EChsPieceType.ROCK, EChsPieceColor.BLACK);
        }
        public ChsPosition(IChsPosition _position)
        {
            colorToMove = _position.ColorToMove;
            allyKingCheck = null;
            whiteCastleQueenSide = _position.PermanentWhiteCastleQueenSide;
            whiteCastleKingSide = _position.PermanentWhiteCastleKingSide;
            blackCastleQueenSide = _position.PermanentBlackCastleQueenSide;
            blackCastleKingSide = _position.PermanentBlackCastleKingSide;
            tempWhiteCastleQueenSide = null;
            tempWhiteCastleKingSide = null;
            tempBlackCastleQueenSide = null;
            tempBlackCastleKingSide = null;
            pieces = new IChsPiece[board.Width, board.Height];
            for (int x = 0; x < board.Width; x++)
                for (int y = 0; y < board.Height; y++)
                    pieces[x,y] = (_position.GetPieceAt(x,y)!=null)?
                        _position.GetPieceAt(x, y).Clone() : null;
        }
        public bool AllyKingCheck
        {
            get
            {
                if (!allyKingCheck.HasValue)
                    allyKingCheck = CalculateAllyKingCheck();
                return allyKingCheck.Value;
            }
        }
        public bool EnemyKingCheck
        {
            get
            {
                if (!enemyKingCheck.HasValue)
                    enemyKingCheck = CalculateEnemyKingCheck();
                return enemyKingCheck.Value;
            }
        }
        public EChsPieceColor ColorToMove
        {
            get
            {
                return colorToMove;
            }
        }
        public bool WhiteCastleQueenSide 
        {
            get
            {
                if (!whiteCastleQueenSide)
                    return false;
                else
                {
                    if (!tempWhiteCastleQueenSide.HasValue)
                        tempWhiteCastleQueenSide = CalculateWhiteCastleQueenSide();
                    return tempWhiteCastleQueenSide.Value;
                }
            }
        }
        public bool WhiteCastleKingSide
        {
            get
            {
                if (!whiteCastleKingSide)
                    return false;
                else
                {
                    if (!tempWhiteCastleKingSide.HasValue)
                        tempWhiteCastleKingSide = CalculateWhiteCastleKingSide();
                    return tempWhiteCastleKingSide.Value;
                }
            }
        }
        public bool BlackCastleQueenSide
        {
            get
            {
                if (!blackCastleQueenSide)
                    return false;
                else
                {
                    if (!tempBlackCastleQueenSide.HasValue)
                        tempBlackCastleQueenSide = CalculateBlackCastleQueenSide();
                    return tempBlackCastleQueenSide.Value;
                }
            }
        }
        public bool BlackCastleKingSide
        {
            get
            {
                if (!blackCastleKingSide)
                    return false;
                else
                {
                    if (!tempBlackCastleKingSide.HasValue)
                        tempBlackCastleKingSide = CalculateBlackCastleKingSide();
                    return tempBlackCastleKingSide.Value;
                }
            }
        }
        public bool PermanentWhiteCastleQueenSide
        {
            get { return whiteCastleQueenSide; }
        }
        public bool PermanentWhiteCastleKingSide
        {
            get { return whiteCastleKingSide; }
        }
        public bool PermanentBlackCastleQueenSide
        {
            get { return blackCastleQueenSide; }
        }
        public bool PermanentBlackCastleKingSide
        {
            get { return blackCastleKingSide; }
        }
        private bool CalculateThereArePiecesPointingAt(EChsPieceColor _color, IChsSquare _square)
        {
            for (int x = 0; x < board.Width; x++)
                for (int y = 0; y < board.Height; y++)
                    if ((pieces[x, y] != null) &&
                        (pieces[x, y].Color == _color) &&
                        validator.ValidateMovementWithoutCheck(this,
                            new ChsMovement(new ChsSquare(x, y), _square)))
                        return true;
            return false; 
        }
        private bool CalculateAllyKingCheck()
        {
            IChsSquare allyKingPosition = null;
            for (int i = 0; i < board.Width; i++)
                for (int j = 0; j < board.Height; j++)
                    if ((pieces[i, j] != null) &&
                        (pieces[i, j].Color == colorToMove) &&
                        (pieces[i, j].Type == EChsPieceType.KING))
                        allyKingPosition = new ChsSquare(i, j);
            return CalculateThereArePiecesPointingAt((colorToMove == EChsPieceColor.BLACK) ?
                EChsPieceColor.WHITE : EChsPieceColor.BLACK, allyKingPosition);
        }
        private bool CalculateEnemyKingCheck()
        {
            IChsSquare enemyKingPosition = null;
            for (int i = 0; i < board.Width; i++)
                for (int j = 0; j < board.Height; j++)
                    if ((pieces[i, j] != null) &&
                        (pieces[i, j].Color != colorToMove) &&
                        (pieces[i, j].Type == EChsPieceType.KING))
                        enemyKingPosition = new ChsSquare(i, j);
            return CalculateThereArePiecesPointingAt(colorToMove, enemyKingPosition);
        }
        private bool CalculateWhiteCastleQueenSide()
        {
            return  (pieces[1,0]==null) &&
                    (pieces[2,0]==null) &&
                    (pieces[3,0]==null) &&
                    (!CalculateThereArePiecesPointingAt(EChsPieceColor.BLACK, new ChsSquare(2,0))) &&
                    (!CalculateThereArePiecesPointingAt(EChsPieceColor.BLACK, new ChsSquare(3,0)));
        }
        private bool CalculateWhiteCastleKingSide()
        {
            return  (pieces[5, 0] == null) &&
                    (pieces[6, 0] == null) &&
                    (!CalculateThereArePiecesPointingAt(EChsPieceColor.BLACK, new ChsSquare(5, 0))) &&
                    (!CalculateThereArePiecesPointingAt(EChsPieceColor.BLACK, new ChsSquare(6, 0)));
        }
        private bool CalculateBlackCastleQueenSide()
        {
            return  (pieces[1, board.Height - 1] == null) &&
                    (pieces[2, board.Height - 1] == null) &&
                    (pieces[3, board.Height - 1] == null) &&
                    (!CalculateThereArePiecesPointingAt(EChsPieceColor.WHITE, new ChsSquare(2, board.Height - 1))) &&
                    (!CalculateThereArePiecesPointingAt(EChsPieceColor.WHITE, new ChsSquare(3, board.Height - 1)));
        }
        private bool CalculateBlackCastleKingSide()
        {
            return (pieces[5, board.Height - 1] == null) &&
                    (pieces[6, board.Height - 1] == null) &&
                    (!CalculateThereArePiecesPointingAt(EChsPieceColor.WHITE, new ChsSquare(5, board.Height - 1))) &&
                    (!CalculateThereArePiecesPointingAt(EChsPieceColor.WHITE, new ChsSquare(6, board.Height - 1)));
        }
        public IChsBoard GetBoard()
        {
            return board;
        }
        public IChsPosition Execute(IChsMovement movement)
        {
            ChsPosition nextPosition = new ChsPosition(this);
            nextPosition.colorToMove = (colorToMove == EChsPieceColor.BLACK) ?
                EChsPieceColor.WHITE :
                EChsPieceColor.BLACK;
            nextPosition.allyKingCheck = null;
            nextPosition.tempWhiteCastleQueenSide = null;
            nextPosition.tempWhiteCastleKingSide = null;
            nextPosition.tempBlackCastleQueenSide = null;
            nextPosition.tempBlackCastleKingSide = null;
            IChsSquare source = movement.Source;
            IChsSquare target = movement.Target;
            //los peones que se podia matar al paso en el turno anterior ya no se puede
            #region desactivar peones al paso
            foreach (ChsPiece piece in nextPosition.pieces)
                if ((piece != null) &&
                    (piece.Color == colorToMove) &&
                    (piece.Type == EChsPieceType.PAWN))
                    ((ChsPawn)piece).UnMakeOnPassant();
            #endregion
            //movimiento básico de las piezas
            #region movimiento básico
            nextPosition.pieces[target.X, target.Y] = nextPosition.pieces[source.X, source.Y];
            nextPosition.pieces[source.X, source.Y] = null;
            #endregion
            //casos especiales del peón
            #region movimiento peones
            //peones al paso
            if ((source.X!=0) &&
                (nextPosition.pieces[source.X - 1, source.Y] != null) &&
                (nextPosition.pieces[source.X - 1, source.Y].Color == nextPosition.colorToMove) &&
                 nextPosition.pieces[source.X - 1, source.Y].OnPassant)
                nextPosition.pieces[source.X - 1, source.Y] = null;
            if ((source.X != (board.Width-1)) &&
                (nextPosition.pieces[source.X + 1, source.Y] != null) &&
                (nextPosition.pieces[source.X + 1, source.Y].Color == nextPosition.colorToMove) &&
                 nextPosition.pieces[source.X + 1, source.Y].OnPassant)
                nextPosition.pieces[source.X + 1, source.Y] = null;
            //al principio, mover 2
            if ((nextPosition.pieces[target.X, target.Y].Type == EChsPieceType.PAWN) &&
                (nextPosition.pieces[target.X, target.Y].Color == EChsPieceColor.WHITE) &&
                (source.X == target.X) &&
                (source.Y == 1) &&
                (target.Y == 3))
            {
                ((ChsPawn)(nextPosition.pieces[target.X, target.Y])).MakeOnPassant();
            }
            if ((nextPosition.pieces[target.X, target.Y].Type == EChsPieceType.PAWN) &&
                (nextPosition.pieces[target.X, target.Y].Color == EChsPieceColor.BLACK) &&
                (source.X == target.X) &&
                (source.Y == board.Height - 2) &&
                (target.Y == board.Height - 4))
            {
                ((ChsPawn)(nextPosition.pieces[target.X, target.Y])).MakeOnPassant();
            }
            //coronación
            if (movement.PromotingType != EChsPieceType.NONE)
                nextPosition.pieces[target.X, target.Y] = new ChsPiece(movement.PromotingType, colorToMove);
            #endregion
            //enroque
            #region enroque
            if ((nextPosition.pieces[target.X, target.Y].Type == EChsPieceType.KING) &&
                (movement.DifX == -2))
            {
                nextPosition.pieces[3, target.Y] = nextPosition.pieces[0, target.Y];
                nextPosition.pieces[0, target.Y] = null;
            }
            if ((nextPosition.pieces[target.X, target.Y].Type == EChsPieceType.KING) &&
                (movement.DifX == 2))
            {
                nextPosition.pieces[5, target.Y] = nextPosition.pieces[7, target.Y];
                nextPosition.pieces[7, target.Y] = null;
            }
            #endregion
            return nextPosition;
        }
        public IChsPiece GetPieceAt(IChsSquare square)
        {
            return pieces[square.X, square.Y];
        }
        public IChsPiece GetPieceAt(int x, int y)
        {
            return pieces[x, y];
        }
        public bool HasAllyPiece(IChsSquare square)
        {
            IChsPiece piece = pieces[square.X, square.Y];
            return (piece != null) && (piece.Color==colorToMove);
        }
        public bool HasPiece(IChsSquare square)
        {
            return pieces[square.X, square.Y] != null;
        }
        public bool HasPiece(int x, int y)
        {
            return pieces[x, y] != null;
        }
        public bool HasEnemyPiece(IChsSquare square)
        {
            IChsPiece piece = pieces[square.X, square.Y];
            return (piece != null) && (piece.Color != colorToMove);
        }
        public bool HasEnemyPiece(int x, int y)
        {
            IChsPiece piece = pieces[x, y];
            return (piece != null) && (piece.Color != colorToMove);
        }
        public bool IsPromotingMovement(IChsSquare source, IChsSquare target)
        {
            return ((pieces[source.X, source.Y] != null) &&
                (pieces[source.X, source.Y].Type == EChsPieceType.PAWN) &&
                (target.Y == ((colorToMove == EChsPieceColor.WHITE) ? 7 : 0)));
        }
    }
}
